#include "protocolconversion.h"
#include "dbointhread.h"
#include "macro.h"
#include <QDebug>
#include <QSqlQuery>
#include <QtSql>

ProtocolConversion::ProtocolConversion(QString DBConectionName, QObject *parent) :
    QObject(parent)
{
#ifdef debu
    qDebug()<<"one instance of ProtocolConversion is created";
#endif
    dboConName=DBConectionName;
    parentDBOInThread=(DBOInThread*)parent;
 }


/****************************************************
*	函数名：QByteArray ProtocolConversion::convertionAppToGWStepOne(const QByteArray &Orignal)
*	作者：  wulin
*	完成时间：
*	函数功能说明：ProtocolConversion的最顶层的成员函数，调用该类的其他各个成员函数。
*	参数：Original，应用程序发给中间件的字符串类型的命令
*       可以有多余的内容，例如“MacTTT”，是有效的。参数对大小写不敏感。
*	返回值：符合《网关节点接口协议》的数据包，可以直接发送给WSN节点。
*******************************************************/
QByteArray ProtocolConversion::convertionAppToGWStepOne(const QByteArray &Orignal,QByteArray baHexAppMac,const QMap <QString, Protocal>  &mapHeadProtocal)
{
    QByteArray result; //result of convertion
    QByteArray temp;
    temp=Orignal.simplified().toUpper();

    QList<QByteArray> list=temp.split(' ');

    baLastSNBase10.append("0");

    if(list.size()>=5&&list.at(0)=="MAC")
    {
        list.removeFirst(); //remove 'mac'

        QByteArray macAddr=list.at(0); //留作后用

        QByteArray head=getHeader(macAddr);

        if(head.startsWith("ERROR"))
            return head;
        else
            head=head;

        if(!mapHeadProtocal.contains(head))
            parentDBOInThread->readProtocolTable(head);

        if(!mapHeadProtocal.contains(head))
            return "ERROR:No suitable protocol in WSN found whose head is "+head;
        else
            currentWSNProtocol=mapHeadProtocal.value(head);


        result.append(head);
        result.replace(currentWSNProtocol.pTypePos/4,currentWSNProtocol.pTypeSize/4,
                       getTYPE("DOWN"));

        result.replace(currentWSNProtocol.pDestPos/4,currentWSNProtocol.pDestSize/4,
                       getDestAddr(list.at(0))); //destination

        list.removeFirst(); //remove mac value
        result.replace(currentWSNProtocol.pSourcePos/4,currentWSNProtocol.pSourceSize/4,
                       getSourceAddr(baHexAppMac));//source

        result.replace(currentWSNProtocol.pSNPos/4,currentWSNProtocol.pSNSize/4,getSN(list.at(0)));
        baLastSNBase10=list.at(0);
        list.removeFirst(); //remove sn value

        temp=getPktType(list.at(0));
        if(temp.startsWith("ERROR"))
            return temp;
        else
            result.replace(currentWSNProtocol.pPkt_TypePos/4,currentWSNProtocol.pPkt_TypeSize/4,temp);

        list.removeFirst();//remove pkt_type value
        result.replace(currentWSNProtocol.pReservedPos/4,currentWSNProtocol.pReservedSize/4,
                       getReserved()); //reserved

        QByteArray cmdQuryType=list.at(0);
        QByteArray sensorType=cmdQuryType.split('_').at(0);

        QSqlQuery tempQuery(QSqlDatabase::database(dboConName));

        if(myCache.hashSensorNameID.contains(sensorType))
        {
            qint64 sensorID=myCache.hashSensorNameID.value(sensorType);
            result.replace(currentWSNProtocol.pSamplePos/4,2,mySetNumHex(sensorID,2));
        }
        else if(!tempQuery.exec("select SensorID from typeMap where upper(SensorName)='"
                               +sensorType+"'"))
        {
            return "ERROR:Failed to get sensorID";
        }
        else if(tempQuery.next())
        {
            qint64 sensorID=tempQuery.value(0).toLongLong();
            myCache.hashSensorNameID.insert(sensorType,sensorID);
            result.replace(currentWSNProtocol.pSamplePos/4,2,mySetNumHex(sensorID,2));
        }
        else
            return "ERROR:Failed to get sensorID";


        if(list.at(0).endsWith("EXCLUSIVESTART"))  //处理冲突问题
        {
            //
               if(controllingMacList.contains(macAddr))
                   return   "ERROR:Server thinks that it already starts now";

               updateControllingMacList(macAddr,true);
               if(!tempQuery.exec("update NodeAddressMap set NodeOccupied=1 where "
                                       "NodeMacAddress="+macAddr))
                   qDebug()<<"Fail to set NodeOccupied"<<tempQuery.lastError().text();
        }

        else if(list.at(0).endsWith("EXCLUSIVESTOP"))  //处理冲突问题
        {
               if(!controllingMacList.contains(macAddr))
                   return   "ERROR: Not started yet.";

               updateControllingMacList(macAddr,false);
               if(!tempQuery.exec("update NodeAddressMap set NodeOccupied=0 where "
                                       "NodeMacAddress="+macAddr))
                   qDebug()<<"Fail to set NodeOccupied"<<tempQuery.lastError().text();
        }

        temp=getData(list);
        if(temp.startsWith("ERROR"))
            return temp;
        else
            result.replace(currentWSNProtocol.pSamplePos/4+2,temp.length(),temp);

        result.append("ffff"); //在最后两字节的CRC位置填充

        QByteArray lenByte=mySetNumHex(result.size()/2-4,
              currentWSNProtocol.pLenSize/4); //数据包长度的单位是字节
        result.replace(currentWSNProtocol.pLenPos/4,currentWSNProtocol.pLenSize/4,lenByte);//to insert len

        result=QByteArray::fromHex(result);
        result.chop(2);
        result.append(QByteArray::fromHex(getCRC16Hex(result.mid(2,result.length()-2)))); //不对头尾进行校验
    }  //if(list.size()>=5&&list.at(0)=="MAC")结束
    else
    {
        result="ERROR: ";
        if(list.size()<5)
        result.append("dataType missing!");
        else
            result.append("It's supposed to begin with 'MAC' or ends with ';' ");
    }
    return result;
}

//list is supposed to be like "Node_stall 1" 、"VideoHik_PTZControlStart PTZ_Cmd"
QByteArray ProtocolConversion::getData(QList<QByteArray> &list)
{
    QSqlQuery tempQuery(QSqlDatabase::database(dboConName));
    QSqlQuery tempQuery2(QSqlDatabase::database(dboConName));

    if(list.size()==0)
        return "ERROR: DataType missing!";

    QByteArray temp=list.at(0);

    QByteArray cmdTypeHex;
    QList<QByteArray> listOfParam;

    bool ok;
    //缓存中没有，且数据库查找失败
    if(!(myCache.hashCmdStringBytes.contains(temp)&&myCache.hashCmdStringEncode.contains(temp))&&
            !tempQuery.exec("select CmdEncode,Bytes from cmdencode where"
                   " upper(CmdString)= \""+temp+"\" ")   )
    {
        return "ERROR: select cmdencod fail in getData()"+tempQuery.lastError().text().toLatin1();
    }
    //缓存中有或者数据库查找成功
    else if((myCache.hashCmdStringBytes.contains(temp)&&myCache.hashCmdStringEncode.contains(temp))||tempQuery.next())
    {
        quint8 bytes;
        //缓存中有
        if(myCache.hashCmdStringBytes.contains(temp)&&myCache.hashCmdStringEncode.contains(temp))
        {
            bytes=myCache.hashCmdStringBytes.value(temp);
            cmdTypeHex=mySetNumHex(myCache.hashCmdStringEncode.value(temp),2*bytes);
        }
        else //缓存中没有，从数据库查询
        {
            bytes=tempQuery.value(1).toUInt();
            cmdTypeHex=mySetNumHex(tempQuery.value(0).toLongLong(),2*bytes);
            myCache.hashCmdStringBytes.insert(temp,bytes);
            myCache.hashCmdStringEncode.insert(temp,tempQuery.value(0).toLongLong());
        }

        //缓存中没有，且数据库操作失败
        if(!(myCache.hashDataTypeParam.contains(list.at(0)))&&
                !tempQuery2.exec("select trim(StringParam) from AvailableCmdQuery where trim(upper(StringData_Type))"
                                  "=\""+list.at(0)+"\""))
            return "ERROR: select StringParam fail in getData(). "+tempQuery2.lastError().text().toLatin1();
            //缓存中有或者查询到了记录
        else if(myCache.hashDataTypeParam.contains(list.at(0))||tempQuery2.next())
        {
            if(myCache.hashDataTypeParam.contains(list.at(0)))
            {
                temp=myCache.hashDataTypeParam.value(list.at(0));
            }
            else
            {
                temp=tempQuery2.value(0).toByteArray().simplified().toUpper();
                myCache.hashDataTypeParam.insert(list.at(0),temp);
            }
            listOfParam=temp.split(' ');
        }      

        list.removeFirst();

        if( ((list.size()==listOfParam.size())&&!temp.isEmpty())||(list.size()==0&&temp.isEmpty()))  //参数数量符合规定
        {
            while(list.size()>0)
            {
                if(!listOfParam.at(0).endsWith("CMD"))
                {
                    if(!(myCache.hashParamType.contains(listOfParam.at(0))&&myCache.hashParamBytes.contains(listOfParam.at(0)))&&
                            !tempQuery2.exec("select trim(Type),Bytes from ParamInfo where"
                                                   " upper(SingleStringParam)= \""+listOfParam.at(0)+"\" "))
                        return "ERROR: select Type,Bytes from ParamInfo fail in getData().Maybe param is wrong"
                                +tempQuery2.lastError().text().toLatin1();
                    else if((myCache.hashParamType.contains(listOfParam.at(0))&&myCache.hashParamBytes.contains(listOfParam.at(0)))||tempQuery2.next())
                    {
                        QByteArray type;
                        quint8 bytes;
                        if(myCache.hashParamType.contains(listOfParam.at(0))&&myCache.hashParamBytes.contains(listOfParam.at(0)))
                        {
                            type=myCache.hashParamType.value(listOfParam.at(0));
                            bytes=myCache.hashParamBytes.value(listOfParam.at(0));
                        }
                        else
                        {
                            type=tempQuery2.value(0).toByteArray();
                            bytes=tempQuery2.value(1).toUInt(&ok);
                            myCache.hashParamType.insert(listOfParam.at(0),type);
                            myCache.hashParamBytes.insert(listOfParam.at(0),bytes);
                        }

                        if(type.toUpper()=="ASCII")
                            cmdTypeHex.append(list.at(0).toHex().leftJustified(bytes*2,'0'));
                        else if(type.toUpper()=="UINT")
                        {
                            cmdTypeHex.append(mySetNumHex(list.at(0).toULong(&ok),bytes*2));
                        }
                    }
                    else
                        return "No Such param";
                } //if(!listOfParam.at(0).endsWith("CMD"))结束
                else  //参数以CMD结尾，如“PTZ_CMD"
                {
                    temp=listOfParam.at(0)+' '+list.at(0);

                    if(!(myCache.hashCmdStringEncode.contains(temp)&&myCache.hashCmdStringBytes.contains(temp))&&
                            !tempQuery2.exec("select CmdEncode,Bytes from CmdEncode where "
                                                      " trim(ucase(CmdString))=\""+temp+"\"")  )
                        return  "select cmdencode fail in getData.Maybe dataType is wrong";

                    else if((myCache.hashCmdStringEncode.contains(temp)&&myCache.hashCmdStringBytes.contains(temp))||tempQuery2.next())
                    {
                        qint64 code;
                        quint8 bytes;
                        if(myCache.hashCmdStringEncode.contains(temp)&&myCache.hashCmdStringBytes.contains(temp))
                        {
                            code=myCache.hashCmdStringEncode.value(temp);
                            bytes=myCache.hashCmdStringBytes.value(temp);
                        }
                        else
                        {
                            code=tempQuery2.value(0).toLongLong(&ok);
                            bytes=tempQuery2.value(1).toUInt(&ok);
                            myCache.hashCmdStringEncode.insert(temp,code);
                            myCache.hashCmdStringBytes.insert(temp,bytes);
                        }
                        cmdTypeHex.append(mySetNumHex(code,2*bytes));
                    }
                    else
                        return "ERROR: no such cmd";
                }

                list.removeFirst();
                listOfParam.removeFirst();

            } //while(list.size()>0)结束
        } //if(list.size()==listOfParam.size()) 结束
        else
            cmdTypeHex="ERROR: param number not correct"; //返回错误信息
    }  //else if(queryForProTocCon->next())结束

    else //缓存中没有，且数据库没有记录
        return "ERROR: no such Data_Type";

    return cmdTypeHex;
}

QByteArray ProtocolConversion::getDestAddr(const QByteArray &destNodeAddr)
{
    QByteArray temp;
    temp=mySetNumHex(destNodeAddr.toULongLong(),currentWSNProtocol.pDestSize/4);
    return temp;
}

QByteArray ProtocolConversion::getHeader(QByteArray nodeMacAddr)
{
    QSqlQuery tempQuery(QSqlDatabase::database(dboConName));

    if(!myCache.hashByteMacProtcHeader.contains(nodeMacAddr)&&
         !tempQuery.exec("select ProtocHead from NodeAddressMap where NodeMacAddress="+nodeMacAddr) )
        return "ERROR: Fail in getHeader()."+tempQuery.lastError().text().toUtf8();
    else if(myCache.hashByteMacProtcHeader.contains(nodeMacAddr)||tempQuery.next())
    {
        QByteArray head;
        if(myCache.hashByteMacProtcHeader.contains(nodeMacAddr))
            head=myCache.hashByteMacProtcHeader.value(nodeMacAddr);
        else
        {
            head=tempQuery.value(0).toByteArray();
            myCache.hashByteMacProtcHeader.insert(nodeMacAddr,head);
        }
        return head;
    }
    else
        return HEADER;
}

/*
QByteArray  ProtocolConversion::getLen()
{
    return "00";
}
*/

QByteArray   ProtocolConversion::getPktType(const QByteArray &PktTypeStr)
{
    QSqlQuery tempQuery(QSqlDatabase::database(dboConName));

    QByteArray temp,pktTypeHex;
    bool ok;
    temp=PktTypeStr;

    if(!myCache.hashCmdStringEncode.contains(temp)&&
        !tempQuery.exec("select CmdEncode from CmdEncode where "
            " trim(upper(CmdString))=\""+temp+"\""))
        return "select cmdencode fail in getPktType";
    else if(myCache.hashCmdStringEncode.contains(temp)||tempQuery.next())
    {
        qint64 code;
        if(myCache.hashCmdStringEncode.contains(temp))
        {
            code=myCache.hashCmdStringEncode.value(temp);
        }
        else
        {
            code=tempQuery.value(0).toLongLong(&ok);
            myCache.hashCmdStringEncode.insert(temp,code);
        }
        pktTypeHex=mySetNumHex(code,currentWSNProtocol.pPkt_TypeSize/4);
        return pktTypeHex;
    }

    return "ERROR: no such Pkt_Type";   //should not be reached
}

QByteArray   ProtocolConversion::getReserved()
{
    return mySetNumHex(1,currentWSNProtocol.pReservedSize/4);
}

QByteArray  ProtocolConversion::getSN(const QByteArray &SNByteArr)
{
    QByteArray temp;
    temp=mySetNumHex(SNByteArr.toULongLong(),currentWSNProtocol.pSNSize/4);
    return temp;
}

QByteArray  ProtocolConversion::getSourceAddr(QByteArray baHexAppMac)
{
    return baHexAppMac.rightJustified(16,'0',true);; //App地址
}


/****************************************************
*	函数名：QByteArray  ProtocolConversion::getTYPE(const QByteArray &typeString)
*	作者：  wulin
*	完成时间：
*	函数功能说明：获得TYPE字段的编码.
*	参数：  typeString,有“UP”和“DOWN”两种。参数中只要包含两者之一即可，
*       可以有多余的内容，例如“Up type”，是有效的。参数对大小写不敏感。
*	返回值：对应type的16进制编码
*******************************************************/
QByteArray  ProtocolConversion::getTYPE(const QByteArray &typeString)
{
    QSqlQuery tempQuery(QSqlDatabase::database(dboConName));

    QByteArray typeTemp=typeString;
    if(!myCache.hashCmdStringEncode.contains(typeTemp)&&
            !tempQuery.exec("select CmdEncode from CmdEncode where CmdString='"+
                    typeTemp+"'")  )
        return "ERROR:Fail to get the Encode of Type"+tempQuery.lastError().text().toUtf8();
    else if(myCache.hashCmdStringEncode.contains(typeTemp)||tempQuery.next())
    {
        qint64 encode;
        if(myCache.hashCmdStringEncode.contains(typeTemp))
            encode=myCache.hashCmdStringEncode.value(typeTemp);
        else
        {
            encode=tempQuery.value(0).toLongLong();
            myCache.hashCmdStringEncode.insert(typeTemp,encode);
        }
        return mySetNumHex(encode,currentWSNProtocol.pTypeSize/4);
    }
    else
        return "ERROR:Fail to get the Encode of Type"+tempQuery.lastError().text().toUtf8();
}


void    ProtocolConversion::updateControllingMacList(QString strMacAddr, bool add)
{
    if(add)
        controllingMacList.append(strMacAddr);
    else
        controllingMacList.removeAll(strMacAddr);
}

QByteArray  ProtocolConversion::convertionGWToAppStepOne(const QByteArray &originalData,Protocal currentWSNProtocol)
{
    bool ok;
    quint64 srcMac=originalData.mid(currentWSNProtocol.pSourcePos/8,currentWSNProtocol.pSourceSize/8)
            .toHex().toULongLong(&ok,16);
    quint64 sN=originalData.mid(currentWSNProtocol.pSNPos/8,currentWSNProtocol.pSNSize/8)
            .toHex().toULongLong(&ok,16);
    quint64 sensorID=originalData.mid(currentWSNProtocol.pSamplePos/8,NodeInfoSensorTypeBytes)
            .toHex().toULongLong(&ok,16);
    quint64 ackID=originalData.mid(currentWSNProtocol.pSamplePos/8+1,1)
            .toHex().toULongLong(&ok,16);
    quint64 statusID=originalData.mid(currentWSNProtocol.pSamplePos/8+2,CmdAckStatusBytes)
            .toHex().toULongLong(&ok,16);

    quint64 stateInt=originalData.mid(currentWSNProtocol.pSamplePos/8+3,originalData.length()-
          currentWSNProtocol.pSamplePos/8-3-2).toHex().toULongLong(&ok,16);
    QByteArray stateStr;
    QByteArray contentType="Decimal String";

    QSqlQuery tempQuery(QSqlDatabase::database(dboConName));

    QSqlQuery tempQuery2(QSqlDatabase::database(dboConName));

    if(ok) //转换成功，当databytearray不大于8字节时，一般会成功
    {

        if(tempQuery.exec("select Expre from NodeSensorMap where NodeMacAddress="+
                                QByteArray::number(srcMac)+" and SensorID="+QByteArray::number(sensorID)))
        {
            if(tempQuery.next())
            {
                QString expre=tempQuery.value(0).toString();
                expre.replace("@data",QString::number(stateInt,10));
                if(!tempQuery2.exec("select "+expre))
                {
                    stateStr=QByteArray::number(stateInt,10);
                }
                else if(tempQuery2.next())
                    stateStr=tempQuery2.value(0).toString().toUtf8();
                else
                    stateStr=QByteArray::number(stateInt,10);
            }
            else
                stateStr=QByteArray::number(stateInt,10);
        }
        else
            stateStr=QByteArray::number(stateInt,10);
    }
    else
    {
        stateStr=originalData.mid(currentWSNProtocol.pSamplePos/8+3,originalData.length()-
               currentWSNProtocol.pSamplePos/8-3-2).toHex(); //大于8字节的情况。存储16进制内容。
        contentType="Hex String";
    }


    QByteArray sensorName=myCache.hashSensorNameID.key(sensorID);
    QByteArray statusStr=hashCmdAckStatusIDMeaning->value(statusID);

    QByteArray result="AckType:CmdAck\n"
            "NodeMac:"+QByteArray::number(srcMac,10)+"\n"
            "SensorName:"+sensorName+"\n"
            "SN:"+QByteArray::number(sN,10)+"\n"
            "Status:"+statusStr+"\n"
            "Content-Type:"+contentType+"\n"
            "State:"+stateStr+"\n";
    return result;

}

ProtocolConversion::~ProtocolConversion()
{
#ifdef debu
    qDebug()<<"one instance of ProtocolConversion is deleted";
#endif
}

